﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using FA.FlightAttendant.Plugin;
using FA.Library.Plugin;
using System.Windows.Input;
using FA.FlightAttendant.View.Commands;
using FA.Library.ExtensionMethods;

namespace FA.FlightAttendant.ViewModel.Windows
{
    public class NewSimulatorVM : ViewModel
    {
        public ICommand SubmitNewSimulatorCommand { get; set; }

        #region Properties

        /// <summary>
        /// The new/edited simulator info.
        /// </summary>
        public NewSimulatorInfo SimulatorInfo
        {
            get { return _simulatorInfo; }
            set
            {
                if (_simulatorInfo != value)
                {
                    _simulatorInfo = value;
                    _simulatorInfo.PropertyChanged += new PropertyChangedEventHandler(NewSimulatorVM_PropertyChanged);
                    NotifyPropertyChanged("SimulatorInfo");
                }
            }
        }
        private NewSimulatorInfo _simulatorInfo;

        /// <summary>
        /// The window title.
        /// </summary>
        public string Title
        {
            get { return _title; }
            set
            {
                if (_title != value)
                {
                    _title = value;
                    NotifyPropertyChanged("Title");
                }
            }
        }
        private string _title;

        /// <summary>
        /// Indicates if we're editing an existing simulator.
        /// </summary>
        public bool EditMode
        {
            get { return (_simulatorInfo != null && !String.IsNullOrEmpty(_simulatorInfo.OriginalName)); }
        }

        /// <summary>
        /// The dialog result which is used to close the window.
        /// </summary>
        public bool? DialogResult
        {
            get { return _dialogResult; }
            set
            {
                if (_dialogResult != value)
                {
                    _dialogResult = value;
                    NotifyPropertyChanged("DialogResult");
                }
            }
        }
        private bool? _dialogResult = null;

        /// <summary>
        /// The available plugins that are loaded.
        /// </summary>
        public ObservableCollection<PluginLibrary> LoadedPlugins
        {
            get { return PluginFactory.Instance.PluginLibraries; }
        }

        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        public NewSimulatorVM()
        {
            SimulatorInfo = new NewSimulatorInfo();
            SubmitNewSimulatorCommand = new SubmitNewSimulatorCommand(this);

            this.PropertyChanged += new PropertyChangedEventHandler(NewSimulatorVM_PropertyChanged);
            
            updateTitle();
        }

        private void updateTitle()
        {
            Title = (EditMode) ? "Edit Simulator" : "New Simulator";
        }

        private void updateSettings()
        {
            ObservableCollection<PluginSetting> settings = new ObservableCollection<PluginSetting>();

            if (SimulatorInfo.Plugin != null)
            {
                // Populate dictionary with the available settings and blank values
                foreach (string setting in SimulatorInfo.Plugin.AvailableSettings)
                {
                    settings.Add(new PluginSetting(setting, String.Empty));
                }
            }

            SimulatorInfo.Settings = settings;
        }

        private void NewSimulatorVM_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Update the window title between new/edit simulator
            if (e.PropertyName.Equals("EditMode"))
            {
                updateTitle();
            }
            else if (e.PropertyName.Equals("Plugin"))
            {
                updateSettings();
            }
        }

        /// <summary>
        /// Check if the settings have been populated.
        /// </summary>
        /// <returns></returns>
        private bool settingsArePopulated()
        {
            if (SimulatorInfo.Settings == null)
                return false;

            foreach (PluginSetting setting in SimulatorInfo.Settings)
            {
                if (String.IsNullOrEmpty(setting.Value.Trim()))
                    return false;
            }
            return true;
        }
    }

    /// <summary>
    /// Class holding the current simulator selection info.
    /// </summary>
    public class NewSimulatorInfo : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }
        private string _name;

        public PluginLibrary Plugin
        {
            get { return _plugin; }
            set
            {
                if (_plugin != value)
                {
                    _plugin = value;
                    NotifyPropertyChanged("Plugin");
                }
            }
        }
        private PluginLibrary _plugin;

        public ObservableCollection<PluginSetting> Settings
        {
            get { return _settings; }
            set
            {
                if (_settings != value)
                {
                    _settings = value;
                    NotifyPropertyChanged("Settings");
                }
            }
        }
        private ObservableCollection<PluginSetting> _settings = new ObservableCollection<PluginSetting>();

        /// <summary>
        /// The original simulator name. This is only set if an existing simulator is being edited,
        /// and is null or empty otherwise.
        /// </summary>
        public string OriginalName
        {
            get { return _originalName; }
            set
            {
                if (value != _originalName)
                {
                    _originalName = value;
                    NotifyPropertyChanged("OriginalName");
                }
            }
        }
        private string _originalName = null;

        /// <summary>
        /// Check if all of the simulator fields have been set.
        /// </summary>
        /// <returns></returns>
        public bool FieldsArePopulated()
        {
            return (Name != null && Name.Trim().Length > 0 && Plugin != null && settingsArePopulated());
        }

        /// <summary>
        /// Check if the settings have been set.
        /// </summary>
        /// <returns></returns>
        private bool settingsArePopulated()
        {
            foreach (PluginSetting setting in Settings)
            {
                if (setting.Value == null || setting.Value.Trim().Length == 0)
                    return false;
            }
            return true;
        }

        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    /// <summary>
    /// Class used to hold the plugin settings as the user enters them.
    /// </summary>
    public class PluginSetting : INotifyPropertyChanged
    {
        /// <summary>
        /// Convert a dictionary of settings to an observable collection of settings.
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public static ObservableCollection<PluginSetting> ConvertFromDictionary(Dictionary<string, string> settings)
        {
            ObservableCollection<PluginSetting> observableSettings = new ObservableCollection<PluginSetting>();
            foreach (KeyValuePair<string, string> setting in settings)
            {
                observableSettings.Add(new PluginSetting(setting.Key, setting.Value));
            }
            return observableSettings;
        }

        /// <summary>
        /// Convert a collection of PluginSetting to a dictionary of settings.
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public static Dictionary<string, string> ConvertToDictionary(ICollection<PluginSetting> settings)
        {
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            foreach (PluginSetting setting in settings)
            {
                dictionary.Add(setting.Name, setting.Value);
            }
            return dictionary;
        }


        public event PropertyChangedEventHandler PropertyChanged;

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Name"));
                }
            }
        }
        private string _name;

        public string Value
        {
            get { return _value; }
            set
            {
                if (_value != value)
                {
                    _value = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
        }
        private string _value;

        public PluginSetting(string name, string value)
        {
            Name = name;
            Value = value;
        }
    }
}
